require "rexml/document"
include REXML

class CtRaidImporter
  
  #Use this mod:
  #http://wow.curse-gaming.com/en/files/details/4197/ct-raidtracker-enggerfreqdkp-modded/

  #assumes reasonable values, xml should be validated before importing it
  #characters can't be assigned drops or attendances unless there's a matching playerinfo element
  #joins and leaves should be coupled, the only discrepancy allowed are:
  #if one join is missing, then a join will be added at the start of the raid
  #if one leave is missing, then a leave will be added at the end of the raid
  #if a join occurs before the start of the raid, it will occur at the start of the raid
  #if a join occurs after the end of the raid, it will occur at the end of the raid
  
  attr_reader :raid
  
  def initialize(raid)
    @raid = raid
    @characters = {}
    @bosses = {}
    @attendances = {}
  end
  
  def import!(text)
    xml = Document.new(text)
    parse(xml)
    unless @raid.valid?
      raise @raid.errors.to_yaml
    end
    @raid.save
    generate_attendances!
    @raid.save
  end
  
  def generate_attendances!
    @attendances.each_pair do |character, joins_and_leaves|
      joins = joins_and_leaves[:joins].sort
      leaves = joins_and_leaves[:leaves].sort
      unless joins.empty? and leaves.empty?
        if joins.size < leaves.size
          joins << @raid.startdate
        elsif leaves.size < joins.size
          leaves << @raid.enddate
        end
        unless joins.size == leaves.size
          raise "Joins and leaves are not coupled for player " + character
        end
        for i in 0..(joins.size-1) do
          if joins[i] > leaves[i]
            raise "Invalid join or leave for player " + character
          end
          @raid.attend(@characters[character], joins[i], leaves[i])
        end
      end
    end
  end
  
  def parse(xml)
    
    unless xml && xml.root
      @errors << "Invalid XML, cannot get root node."
      return
    end
    
    xml.root.elements.each do |element|
      case element.name.downcase
      when "key"
        #not going to use this.
      when "start"
        parse_start(element)
      when "end"
        parse_end(element)
      when "zone"
        parse_zone(element)
      when "playerinfos"
        parse_playerinfos(element)
      when "bosskills"
        parse_bosskills(element)
      when "note"
        parse_note(element)
      when "join"
        parse_joins(element)
      when "leave"
        parse_leaves(element)
      when "loot"
        parse_loot(element)
      else
        @errors << "Don't know how to parse " + element.name
      end
    end
    
  end
  
  def parse_start(xml)
    @raid.startdate = Time.parse(xml.text)
  end
  
  def parse_end(xml)
    @raid.enddate = Time.parse(xml.text)
  end
  
  def parse_zone(xml)
    @raid.zone = Zone.find_or_create_by_name(xml.text)
  end
  
  def parse_playerinfos(xml)
    xml.elements.each do |element|
      parse_playerinfo(element)
    end
  end
  
  def parse_playerinfo(xml)
    name = xml.elements["name"].text
    race = xml.elements["race"].text.humanize
    chrclass = xml.elements["class"].text.humanize
    
    character = Character.find_by_name(name)
    unless character
      character = Character.new
      character.name = name
      character.race = Race.find_by_name(race)
      character.chrclass = Chrclass.find_by_name(chrclass)
      unless character.valid?
        raise "Invalid character: " + character.inspect
      end
      character.save
    end
    
    @attendances[name] = {:joins => [], :leaves => []}
    @characters[name] = character
  end
  
  def parse_bosskills(xml)
    xml.elements.each do |element|
      parse_bosskill(element)
    end
  end
  
  def parse_bosskill(xml)
    unless xml.elements["name"]  && xml.elements["name"].text && xml.elements["time"] && xml.elements["time"].text
      raise "Invalid bosskill: " + self.inspect
    end
    
    name = xml.elements["name"].text
    event = BossEvent.find_by_name(name)
    unless event
      event = BossEvent.create(:name => name)
    end
    
    raid_event = RaidEvent.new
    raid_event.event = event
    raid_event.raid = @raid
    raid_event.time = Time.parse(xml.elements["time"].text)
    
    @bosses[name] = raid_event
    @raid.raid_events << raid_event
  end
  
  def parse_note(xml)
    unless @raid.notes
      @raid.notes = ""
    end
    @raid.notes << xml.text << "\n"
  end
  
  def parse_joins(xml)
    xml.elements.each do |element|
      parse_join_or_leave(element)
    end
  end
  
  def parse_leaves(xml)
    xml.elements.each do |element|
      parse_join_or_leave(element)
    end
  end
  
  def parse_join_or_leave(xml)
    unless xml.elements["time"] and xml.elements["time"].text and xml.elements["player"] and xml.elements["player"].text
      raise "Invalid join or leave: " + self.inspect
    end
    time = Time.parse(xml.elements["time"].text)
    player = xml.elements["player"].text
    unless @characters[player]
      raise "Invalid character for join or leave: " + self.inspect
    end
    if xml.parent.name.downcase == "join"
      if time < @raid.startdate
        time = @raid.startdate
      end
      @attendances[player][:joins] << time
    elsif xml.parent.name.downcase == "leave"
      if time > @raid.enddate
        time = @raid.enddate
      end
      @attendances[player][:leaves] << time
    else
      raise "Invalid join or leave: " + self.inspect
    end
  end
  
  def parse_loot(xml)
    xml.elements.each do |element|
      parse_drop(element)
    end
  end
  
  def parse_drop(xml)
    unless xml.elements["ItemID"] && xml.elements["ItemID"].text && xml.elements["Player"] && xml.elements["Player"].text
      raise "Invalid loot: " + self.inspect
    end

    character = @characters[xml.elements["Player"].text]
    unless character
      raise "Invalid player for loot: " + self.inspect
    end

    raid_event = nil
    if xml.elements["Boss"] and xml.elements["Boss"].text
      boss = xml.elements["Boss"].text
      raid_event = @bosses[boss]
    end

    wowid = Item.parseWowID(xml.elements["ItemID"].text)
    item = Item.find_by_wowlink(wowid)
    unless item
      unless xml.elements["ItemName"] and xml.elements["ItemName"].text
        raise "Invalid item for loot: " + self.inspect
      end
      
      item = Item.new
      item.name = xml.elements["ItemName"].text
      item.wowlink = wowid
      if xml.elements["Icon"] && xml.elements["Icon"].text
        item.iconname = xml.elements["Icon"].text
      end
      item.save
    end
    
    drop = ItemDrop.new
    drop.character = character
    drop.item = item
    drop.raid = @raid
    if raid_event
      drop.raid_event = raid_event
    end
    
    @raid.item_drops << drop
  end
  
end